package com.example.Arithmetic.Arithmetic;

import com.example.Arithmetic.Arithmetic.Port.BlockingQueue;

import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.ReentrantLock;

/**
 * 日期：2023/10/11
 * 时间：9:10
 * 描述：双锁实现线程安全的队列
 * 解决size线程安全问题，单锁写入和移除不可同时进行问题,还保证了通知对方时不会发生死锁问题
 * 通知对方需要有配套的锁同时使用，不然会报错，锁嵌套在一起就会发生死锁问题
 */
public class BlockingQueueDoubleLock<E> implements BlockingQueue<E> {

    E[] array;
    int head;
    int tail;
    AtomicInteger size = new AtomicInteger(0);

    public BlockingQueueDoubleLock(int capacity) {
        array = (E[]) new Object[capacity];
    }

    ReentrantLock HeadLock = new ReentrantLock();
    ReentrantLock TailLock = new ReentrantLock();
    Condition HeadWaits = HeadLock.newCondition();
    Condition TailWaits = TailLock.newCondition();

    @Override
    public void offer(E e) throws InterruptedException {
        TailLock.lockInterruptibly();
        int c;
        try {
            while (isFull()) {
                TailWaits.await();
            }
            array[tail] = e;
            if (++tail == array.length) {
                tail = 0;
            }
            c = size.getAndIncrement();
            //第一个offer线程唤醒其他阻塞线程
            if (c + 1 < array.length) {
                TailWaits.signal();
            }
        } finally {
            TailLock.unlock();
        }
//        和上一个try平级就不会发生死锁问题
        if (c == 0) {                       //c此时是自增前的值，从无变为有，就会通知poll线程
            HeadLock.lock();
            try {
                HeadWaits.signal();          //通知移除方法锁有数据了
            } finally {
                HeadLock.unlock();
            }
        }
    }

    @Override
    public boolean offer(E e, long timeout) throws InterruptedException {
        int c;
        TailLock.lockInterruptibly();
        long l = TimeUnit.MILLISECONDS.toNanos(timeout);
        try {
            while (isFull()) {
                if (l <= 0) {
                    return false;
                }
                l = TailWaits.awaitNanos(l);
            }
            array[tail] = e;
            if (++tail == array.length) {
                tail = 0;
            }
            c = size.getAndIncrement();
            if (c + 1 < array.length) {
                TailWaits.signal();
            }
        } finally {
            TailWaits.signal();
        }
//        减少唤醒次数，从0到多选择数组在0个元素时候第一个offer线程通知poll
        if (c == 0) {
            HeadLock.lock();
            try {
                HeadWaits.signal();
            } finally {
                HeadLock.unlock();
            }
        }
        return true;
    }

    @Override
    public E poll() throws InterruptedException {
        HeadLock.lockInterruptibly();
        int c;
        E e;
        try {
            while (isEmpty()) {
                HeadWaits.await();
            }
            e = array[head];
            array[head] = null;   //help GC
            if (++head == array.length) {
                head = 0;
            }
            c = size.getAndDecrement();
            //证明其中还有元素，因此通知其他线程
            if (c > 1) {
                HeadWaits.signal();
            }
        } finally {
            HeadLock.unlock();
        }
//        c是自减之前的值，等于数组长度就证明从满到不满，唤醒offer添加线程
        if (c == array.length) {
            TailLock.lock();
            try {
                TailWaits.signal();            //通知新增方法有空位了
            } finally {
                TailLock.unlock();
            }
        }
        return e;
    }
    private boolean isEmpty() {
        return size.get() == 0;
    }
    private boolean isFull() {
        return size.get() == array.length;
    }
}
